use std::{fmt, path::StripPrefixError, sync::Arc, any::Any};
use crate::{recipe::{RecipeError, VersionError}, index::error::IndexError, container::ContainerError};
pub type BackupResult<T> = Result<T,BackupError>;

#[derive(Debug)]
pub enum BackupError {
    WriteLargeChunk(usize),
    Entry(walkdir::Error),
    EntryMetadata(std::io::Error),
    PathMetadata(String,std::io::Error),
    StripPrefix(StripPrefixError),
    FileOpen(String,std::io::Error),
    FileRead(String,std::io::Error),
    UnexpectedWriteError(Box<dyn Any + Send>),
    Index(IndexError),
    Collect(BackupErrorCollector),
    Container(ContainerError),
    Version(VersionError),
    Recipe(RecipeError),
}

impl fmt::Display for BackupError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            BackupError::WriteLargeChunk(len) => write!(f,"Error chunk size(0x{:0x}) is more than container size(0x{:0x})"
                                                        ,len,crate::constants::CONTAINER_SIZE),
            BackupError::PathMetadata(file,e) => write!(f,"Error {} metadata acquire failuer:{}",file,e),
            BackupError::EntryMetadata(e) => write!(f,"Error metadata acquire failuer:{}",e),
            BackupError::Entry(e) => write!(f,"Error entry acquire failuer:{}",e),
            BackupError::FileOpen(file,e) => write!(f,"Error file {} open failure:{}",file,e),
            BackupError::FileRead(file,e) => write!(f,"Error file {} read failure:{}",file,e),
            BackupError::Index(e) => write!(f,"index::{}",e),
            BackupError::UnexpectedWriteError(_) => write!(f,"unexpected write phase error"),
            BackupError::Container(e) => write!(f,"container::{}",e),
            BackupError::StripPrefix(e) => write!(f,"Error strip prefix:{}",e),
            BackupError::Version(e) => write!(f,"version::{}",e),
            BackupError::Recipe(e) => write!(f,"recipe::Error {}",e),
            BackupError::Collect(e) => write!(f,"{}",e),
        }
    }
}

impl std::error::Error for BackupError {}

impl From<RecipeError> for BackupError {
    fn from(value: RecipeError) -> Self {
        Self::Recipe(value)
    }
}

impl From<VersionError> for BackupError {
    fn from(value: VersionError) -> Self {
        Self::Version(value)
    }
}

impl From<StripPrefixError> for BackupError {
    fn from(value: StripPrefixError) -> Self {
        Self::StripPrefix(value)
    }
}

impl From<IndexError> for BackupError {
    fn from(value: IndexError) -> Self {
        Self::Index(value)
    }
}

impl From<ContainerError> for BackupError {
    fn from(value: ContainerError) -> Self {
        Self::Container(value)
    }
}

impl From<BackupErrorCollector> for BackupError {
    fn from(value: BackupErrorCollector) -> Self {
        Self::Collect(value)
    }
}

impl From<Box<dyn Any + Send>> for BackupError {
    fn from(value: Box<dyn Any + Send>) -> Self {
        Self::UnexpectedWriteError(value)
    }
}


#[derive(Debug,Clone)]
pub struct BackupErrorCollector {
    errs:Arc<parking_lot::Mutex<Vec<BackupError>>>,
}

impl BackupErrorCollector {
    pub fn new() -> Self {
        Self { errs:Arc::new(parking_lot::Mutex::new(Vec::new())) }
    }

    pub fn collect<T>(&self,t:BackupResult<T>) {
        if let Err(e) = t {self.errs.lock().push(e);}
    }

    pub fn try_return<T>(self,t:T) -> BackupResult<T> {
        let result = self.errs.lock().is_empty();
        if result {
            Ok(t)
        } else {
            Err(self.into())
        }
    }
}

impl fmt::Display for BackupErrorCollector {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let errs = self.errs.lock();
        if errs.len() == 1 { return write!(f,"{}",errs[0]); }
        let mut info = "".to_string();
        for err in errs.iter() {
            info += format!("\n\t{}",err).as_str();
        }
        write!(f,"{}",info)
    }
}


